<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>饼图</title>
  <style type="text/css">
    #pie {
      border: 1px solid #ccc;
      border-radius: 10px;
    }
  </style>
</head>

<body>
  <canvas id="pie" width="600" height="600"></canvas>
  <script type="text/javascript">
    (function () {
      let canvas = document.getElementById('pie');
      canvas.setAttribute('width', (document.documentElement.clientWidth || document.body.clientWidth) - 500);
      let ctx = canvas.getContext('2d');
      let width = canvas.clientWidth;
      let height = canvas.clientHeight;
      let widthHalf = width / 2;
      let heightHalf = height / 2;
      let colors = ["#00B2EE", "#A4D3EE", "#9400D3", "#FF00FF", "#EEB422", "#ADFF2F"];

      let data = [
        { value: 1000, label: "苏宁易购" },
        { value: 500, label: "东方甄选" },
        { value: 1000, label: "淘宝精选" },
        { value: 8000, label: "京东商城" },
        { value: 1000, label: "拼 多 多" },
        { value: 300, label: "亚 马 逊" }
      ]

      let radius = heightHalf / 2;
      function init() {
        ctx.beginPath()
        ctx.moveTo(widthHalf, heightHalf);
        ctx.fillStyle = '#ffffff'
        ctx.fillRect(0, 0, width, height);
        ctx.fill();
        ctx.imageSmoothingQuality = "high";
        draw();
        rander();
        bindEvent();
      }

      function rander() {
        // 数据总数
        let dataTotal = data.reduce((prev, item) => {
          prev += item.value;
          return prev;
        }, 0)


        data.reduce((prev, item, index, arr) => {
          let curDeg = (item.value / dataTotal * 360) * (Math.PI / 180);

          // 绘制饼图
          ctx.beginPath();
          ctx.fillStyle = colors[index];
          ctx.moveTo(widthHalf, heightHalf);
          if (!index) {
            ctx.arc(widthHalf, heightHalf, radius, prev, curDeg);
          } else {
            ctx.arc(widthHalf, heightHalf, radius, prev, 0);
          }
          ctx.fill();

          // 绘制图例
          let color = colors[index];
          ctx.fillStyle = color;
          ctx.beginPath();
          if (item.label.length === 3) {
            ctx.letterSpacing = '.5px'
          }
          ctx.font = 'bold 13px cursive'
          ctx.fillText(`${index + 1}. ${item.label} : ${item.value} 件`, 30, (index + 2) * 30, 200);
          ctx.fill();
          prev += curDeg;
          return prev;
        }, 0)

        // 绘制tooltip
        data.reduce((prev, item, index) => {
          let curDeg = item.value / dataTotal * 360;
          let color = colors[index];
          // prev 是本次开始的度 prev + curDeg是本次要到达的度数
          /**
           * 1.pre 0    cur 100
           * 2.pre 100  cur 20  那么第二部的开始度数是100 结束度数就是 100 + 20
           * 3/pre 120  cur 20  那么第三部的开始度数就是120 结束度数就是120 + 20
           **/

          let { x, y } = calculateArcCenter(widthHalf, heightHalf, radius + 90, prev, prev + curDeg);
          let moveParams = calculateArcCenter(widthHalf, heightHalf, radius / 2, prev, prev + curDeg);
          // 绘制文本
          ctx.beginPath();
          ctx.fillStyle = '#000000';
          if (item.label.length === 3) {
            ctx.letterSpacing = '.5px'
          }
          ctx.font = 'bold 20px cursive'
          ctx.fillText(`${index + 1}. ${item.label} : ${item.value} 件`, x - 50, y, 200);
          ctx.fill();

          // 绘制指示线
          ctx.beginPath();
          ctx.strokeStyle = '#00000070'
          ctx.moveTo(moveParams.x, moveParams.y)
          ctx.lineTo(x, y);
          ctx.stroke();

          // 绘制指示线饼图中小圆点
          ctx.beginPath();
          ctx.fillStyle = '#000000'
          ctx.moveTo(moveParams.x, moveParams.y);
          ctx.arc(moveParams.x, moveParams.y, 2, 0, 360);
          ctx.fill();

          // 绘制指示线指示文本中小圆点
          ctx.beginPath();
          ctx.fillStyle = '#000000'
          ctx.moveTo(moveParams.x, moveParams.y);
          ctx.arc(x, y, 2, 0, 360);
          ctx.fill();

          prev += curDeg;
          return prev;
        }, 0)

        // 绘制图例框
        ctx.beginPath();
        ctx.strokeStyle = '#cccccc';
        ctx.strokeRect(20, 10, 150, (data.length + 1) * 30);
        ctx.stroke();
        ctx.beginPath();
        ctx.fillStyle = "#000000";
        ctx.font = 'bold 20px cursive'
        ctx.fillText("数据图例", 30, 30);
      }


      function calculateArcCenter(centerX, centerY, radius, startAngle, endAngle) {
        // 将角度转换为弧度  高中数学
        var startAngleRad = startAngle * Math.PI / 180;
        var endAngleRad = endAngle * Math.PI / 180;
        // 计算中心角  两个弧度中心度
        var centerAngleRad = (startAngleRad + endAngleRad) / 2;
        // 计算中心点的坐标  高中数学
        var centerX = centerX + radius * Math.cos(centerAngleRad);
        var centerY = centerY + radius * Math.sin(centerAngleRad);
        return { x: centerX, y: centerY };
      }

      // 角度计算公式（ 角度 *(pi/180) ）
      function draw() {
        ctx.fillStyle = '#cccccc50';
        ctx.beginPath();
        ctx.moveTo(widthHalf, heightHalf);
        ctx.arc(widthHalf, heightHalf, radius, 0, 360 * (Math.PI / 180));
        ctx.closePath()
        ctx.fill();
      }

      function bindEvent() {
      }
      function generateHexColor(r, g, b) {
        r = r.toString(16);
        g = g.toString(16);
        b = b.toString(16);

        if (r.length == 1)
          r = "0" + r;
        if (g.length == 1)
          g = "0" + g;
        if (b.length == 1)
          b = "0" + b;

        return "#" + r + g + b;
      }
      init();
    })()
  </script>
</body>

</html>